#include <QtCore/qfileinfo.h>
#include <QtCore/qurl.h>
#include <QDateTime>
#include <QDebug>
#include <QMap>
#include <QMessageBox>
#include <QMultiMap>
#include <qmediaplaylist.h>
#include "playlistmodel.h"
#include "abstractmetadataloader.h"
#include "urlutils.h"
#include "mainwindow.h"

using namespace Abstract;

PlaylistModel::PlaylistModel(QObject *parent)
    : QAbstractListModel(parent)
    , m_playlist(0)
{
    m_isFetched = false;
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[PictureRole] = "picture";
    roles[UrlRole] = "url";
    roles[ArtistRole] = "artist";
    roles[LastModifiedRole] = "lastModified";
    roles[GenreRole] = "genre";
    roles[AlbumRole] = "album";
    roles[TrackNumberRole] = "trackNr";
    roles[CommentRole] = "comment";
    roles[DurationRole] = "duration";
    // m_isSorted = false;
    setRoleNames(roles);

    connect (MetaDataLoader::instance(), SIGNAL(fetched(MetaDataValue)),
        this, SLOT(fetched(MetaDataValue)), Qt::UniqueConnection);

}

int PlaylistModel::rowCount(const QModelIndex &) const
{
    return m_playlist ? m_playlist->mediaCount() : 0;
}


QVariant PlaylistModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid()) return QVariant();
    if ((index.row() < 0) || (index.row() >= rowCount())) return QVariant();

    QMediaContent mc = m_playlist->media(index.row());
    QUrl location = mc.canonicalUrl();

    MetaDataValue mdv;
    if (m_values.contains(location) )
        mdv = m_values[location];
    else {
        mdv.setUrl(location);
        QString path = UrlUtils::toLocalFile(location);
        QFileInfo finfo(path);
        mdv.setTrackTitle(UrlUtils::baseName(finfo));
    }

    if ((role == Qt::DisplayRole) || (role == NameRole))  {
        if (mdv.artist().isEmpty())
            return mdv.trackTitle();
        else return QString("%2 - %1").arg(mdv.artist()).arg(mdv.trackTitle());
    }
    if (role == TrackNumberRole) {
        return mdv.trackNumber();
    }
    if (role == DurationRole) {
        return mdv.trackTime();
    }
    if (role == GenreRole) {
        return mdv.genre();
    }
    if (role == ArtistRole) {
        return mdv.artist();
    }
    if (role == CommentRole) {
        return mdv.comment();
    }
    if (role == AlbumRole) {
        return mdv.albumTitle();
    }
    if (role == LastModifiedRole) {
        return mdv.lastModified();
    }
    if ((role == Qt::ToolTipRole) || (role == UrlRole) || (role == Qt::StatusTipRole)) {
        return mdv.url();
    }
    // TODO  - handle decoration role
    return QVariant();
}

QMediaPlaylist *PlaylistModel::playlist() const
{
    return m_playlist;
}

void PlaylistModel::setPlaylist(QMediaPlaylist *playlist)
{
    if (m_playlist) {
        disconnect(m_playlist, SIGNAL(mediaAboutToBeInserted(int,int)), this, SLOT(beginInsertItems(int,int)));
        disconnect(m_playlist, SIGNAL(mediaInserted(int,int)), this, SLOT(endInsertItems()));
        disconnect(m_playlist, SIGNAL(mediaAboutToBeRemoved(int,int)), this, SLOT(beginRemoveItems(int,int)));
        disconnect(m_playlist, SIGNAL(mediaRemoved(int,int)), this, SLOT(endRemoveItems()));
        disconnect(m_playlist, SIGNAL(mediaChanged(int,int)), this, SLOT(changeItems(int,int)));
    }

    m_playlist = playlist;

    if (m_playlist) {
        connect(m_playlist, SIGNAL(mediaAboutToBeInserted(int,int)), this, SLOT(beginInsertItems(int,int)));
        connect(m_playlist, SIGNAL(mediaInserted(int,int)), this, SLOT(endInsertItems()));
        connect(m_playlist, SIGNAL(mediaAboutToBeRemoved(int,int)), this, SLOT(beginRemoveItems(int,int)));
        connect(m_playlist, SIGNAL(mediaRemoved(int,int)), this, SLOT(endRemoveItems()));
        connect(m_playlist, SIGNAL(mediaChanged(int,int)), this, SLOT(changeItems(int,int)));
    }
    reset();
}


void PlaylistModel::beginInsertItems(int start, int end) {
    m_indexes.clear();
    beginInsertRows(QModelIndex(), start, end);
}

void PlaylistModel::endInsertItems() {
    endInsertRows();
}

void PlaylistModel::beginRemoveItems(int start, int end) {
    m_indexes.clear();
    beginRemoveRows(QModelIndex(), start, end);
}

void PlaylistModel::endRemoveItems() {
    endInsertRows();
}

void PlaylistModel::changeItems(int start, int end) {
    m_indexes.clear();
    emit dataChanged(index(start,0), index(end,1));
}

int PlaylistModel::findRow(QUrl key) {
    if (m_indexes.contains(key)) return m_indexes[key];
    int retval = -1;
    for (int r=0; r<rowCount(); ++r) {
        QUrl u = data(index(r,0), UrlRole).toString();
        m_indexes[u] = r;
        if (u == key) retval = r;
    }
    return retval;
}

void PlaylistModel::shuffle() {
    m_playlist->shuffle();
    reset();
}
void PlaylistModel::fetched(MetaDataValue mdv) {
    m_values[mdv.url()]=mdv;
//    qDebug() << "fetched: " << mdv.url();
    int r = findRow(mdv.url());
    if (r != -1) {
        QModelIndex tl = index(r,0, QModelIndex());
        emit dataChanged(tl, tl);
    }
}
void PlaylistModel::clear() {
    m_playlist->clear();
    m_isFetched=false;
    reset();
}

void PlaylistModel::sortByRole(PlayListRoles role) {
    if (!m_isFetched) {
        fetchAll();
        return;
    }
    m_indexes.clear();
    if (role == LastModifiedRole) {
        QMultiMap<QDateTime, QUrl> mapping;
        for (int i=0; i<rowCount(); ++i) {
           QUrl url = m_playlist->media(i).canonicalUrl();
           MetaDataValue mdv = m_values[url];
           mapping.insert(mdv.lastModified(), url);
        }
        m_playlist->clear();
        QMultiMap<QDateTime, QUrl>::const_iterator itr = mapping.constEnd();
        while (itr != mapping.constBegin()) {
            --itr;
            QUrl url = itr.value();
            m_playlist->addMedia(QMediaContent(url));
        }
        reset();
        return;
    }
    QMultiMap<QString, QUrl> mapping;
    QRegExp rx("(\\d+).*");
    for (int i=0; i<rowCount(); ++i) {
       QUrl url = m_playlist->media(i).canonicalUrl();
       MetaDataValue mdv = m_values[url];
       QString key = mdv.trackTitle();
       if (role == AlbumRole) {
           QString tn = mdv.trackNumber();
           if (rx.exactMatch(tn))
               tn = rx.cap();
           int trackNumber = tn.toInt();
           if (trackNumber > 0)
               key = QString("%1-%2").arg(mdv.albumTitle()).arg(trackNumber, 4, QChar('0'));
           else key = QString("%1-%2").arg(mdv.albumTitle()).arg(mdv.trackTitle());
       }
       else if (role == ArtistRole) {
           key = QString("%1-%2").arg(mdv.artist()).arg(mdv.trackTitle());
       }
       else if (role == NameRole) {
           key = mdv.trackTitle();
       }
       mapping.insert(key, url);
    }
    m_playlist->clear();
    QMultiMap<QString, QUrl>::const_iterator itr = mapping.constBegin();
    while (itr != mapping.constEnd()) {
       QUrl url = itr.value();
       m_playlist->addMedia(QMediaContent(url));
       itr++;
    }
    reset();
}

void PlaylistModel::setFetched(bool f) {
    m_isFetched = f;
    if (f == true)
        MetaDataLoader::instance()->disconnect(this, SLOT(setFetched()));

}

void PlaylistModel::deleteTrack(int i) {
    beginRemoveItems(i, i);
    QMediaContent mc = m_playlist->media(i);
    QString fileName = UrlUtils::toLocalFile(mc.canonicalUrl());
    int response = QMessageBox::question(MainWindow::instance(),
        tr("Delete File"), tr("delete ") + fileName, QMessageBox::No, QMessageBox::Yes);
    if (response == QMessageBox::Yes) {
        m_playlist->removeMedia(i, i);
        QFile f(fileName);
        f.remove();
    }
    endRemoveItems();
    reset();
}

void PlaylistModel::fetchAll() {
    MetaDataLoader *mdl = MetaDataLoader::instance();
    connect (mdl, SIGNAL(finished()), this, SLOT(setFetched()));

    int r = rowCount();
    int c = 0;
    for (int i=0; i<r; ++i) {
        QMediaContent mc = m_playlist->media(i);
        QUrl url = mc.canonicalUrl();
        if (m_values.contains(url)) continue;
        mdl->get(url);
        c++;
    }
    if (c == 0) m_isFetched = true;
}

void PlaylistModel::fetch(int i) {    
    MetaDataLoader *mdl = MetaDataLoader::instance();
    QMediaContent mc = m_playlist->media(i);
    mdl->get(mc.canonicalUrl());
}

